package com.hmdp.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmdp.dto.LoginFormDTO;
import com.hmdp.dto.Result;
import com.hmdp.dto.UserDTO;
import com.hmdp.entity.User;
import com.hmdp.mapper.UserMapper;
import com.hmdp.service.IUserService;
import com.hmdp.utils.RegexUtils;
import com.hmdp.utils.SystemConstants;
import com.hmdp.utils.UserHolder;
import lombok.extern.slf4j.Slf4j;
import lombok.extern.slf4j.XSlf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpSession;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import static com.hmdp.utils.RedisConstants.*;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 钟乐
 * @since 2021-12-22
 */
@Slf4j
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

    @Resource
    StringRedisTemplate stringRedisTemplate;

    @Override
    public Result sendCode(String phone, HttpSession session) {
        //校验手机号,使用RegexUtils直接验证手机号格式是否正确
        //session在请求头里面
        if(RegexUtils.isPhoneInvalid((phone))){
            return Result.fail("手机号格式错误");
        }

        //不符合返回错误信息，RandomUtil生成验证码
        String code = RandomUtil.randomNumbers((6));

        //符合生成验证码,保存到redis
        stringRedisTemplate.opsForValue().set(LOGIN_CODE_KEY+phone,code,LOGIN_CODE_TTL,TimeUnit.MINUTES);

        //保存验证码到session
        log.debug("发送短信验证码成功，{}",code);

        //返回OK

        return Result.ok();
    }

    @Override
    public Result login(LoginFormDTO loginForm, HttpSession session) {

        //短信登陆包括注册和登陆

        //校验手机好
        String phone = loginForm.getPhone();
        if(RegexUtils.isPhoneInvalid(phone)){
            return Result.fail("手机号格式错误");
        }

        //校验验证码,从redis中获取数据
        String cacheCode = stringRedisTemplate.opsForValue().get(LOGIN_CODE_KEY+phone);
        String code = loginForm.getCode();
        //使用反向判断，避免了层层叠加if
        if (cacheCode==null || !code.equals(cacheCode)){
            //不一致报错
            return Result.fail("验证码错误");
        }

        //一致，去数据库查询用户
        User user = query().eq("phone", phone).one();

        //判断用户是否存在
        if (user == null) {

            //不存在，注册用户
             user = createUserWithPhone(phone);
        }

        //这里不能存用户的铭感信息，存个UserDTO就好了
        //保存用户信息到redis，
        // 生成token让前端保存到header中,之后每次访问通过header拿到用户信息
        String token = UUID.randomUUID().toString(true);

        //hash存储对象信息
        UserDTO userDTO = BeanUtil.copyProperties(user, UserDTO.class);
        userDTO.setToken(token);

        Map<String, Object> userMap = BeanUtil.beanToMap(userDTO,new HashMap<>(),
                CopyOptions.create().setIgnoreNullValue(true).setFieldValueEditor((fieldName,fieldValue)->fieldValue.toString()));
        String tokenKey = LOGIN_USER_KEY+token;

        //putAll将map全部保存进去
        stringRedisTemplate.opsForHash().putAll(tokenKey, userMap);
        stringRedisTemplate.expire(tokenKey,LOGIN_USER_TTL,TimeUnit.DAYS);  //设置过期时间，需要添加延时

        //返回token给前端保存到header里去
        return Result.ok(token);
    }

    @Override
    public Result sign() {

        Long userId = UserHolder.getUser().getId();

        //拼接key
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = "sign:" + userId + keySuffix;
        //获取日期,今天是这个月的第几天
        int dayOfMonth = now.getDayOfMonth();

        //写入redis setbit key offset 1
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);

        return Result.ok();
    }

    @Override
    public Result signCount() {

        Long userId = UserHolder.getUser().getId();

        //获取key
        LocalDateTime now = LocalDateTime.now();
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = "sign:"+userId+keySuffix;

        //获取天
        int dayOfMonth = now.getDayOfMonth();

        //先获取本月到目前的所有签到记录
        List<Long> result = stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create()
                .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));

        if(result.isEmpty() || result==null){
            return Result.ok(0);
        }

        Long num = result.get(0);

        if(num==null || num==0){
            return Result.ok(0);
        }
        //while(true) + 与运算统计
        int count = 0;
        while (true){
            //循环判断，是否读到0，读到0就break
            if((num & 1) == 0){
                break;
            }else {
                count++;
                num >>>= 1;  //>>>右移动 num = num >>>= 1;
            }
        }
        return Result.ok(count);
    }


    private User createUserWithPhone(String phone) {
        User user = new User();
        user.setPhone(phone);
        user.setNickName(SystemConstants.USER_NICK_NAME_PREFIX+RandomUtil.randomString(10));
        save(user);
        return user;
    }

}
